package com.szlanyou.tcpservermaster;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;

import com.szlanyou.tcpservermaster.exception.ClientOffLineException;
import com.szlanyou.tcpservermaster.util.DoneLogger;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * @author 陈章
 * create at 2021/1/19 8:57
 * desc:Socket服务端的客户端处理封装类
 */
public class TcpClientItem {
    private static final String TAG = "TcpClientItem";
    /**
     * 主动断开标记
     */
    private boolean isRun = false;
    /**
     * 是否是异常，导致被动关闭客户端。
     */
    private boolean isExceptionClosedClient = false;

    private Socket client;
    private DataOutputStream out;
    private InputStream in;
    private OnClientStatus onClientStatus;
    private String address;
    private Handler childHandler;

    private final int MSG_TYPE_INIT = 10;
    private final int MSG_TYPE_SEND = 20;
    private final int MSG_TYPE_READ = 30;
    /**
     * 该callback运行于子线程
     */
    private class ChildCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case MSG_TYPE_INIT:
                    init();
                    childHandler.obtainMessage(MSG_TYPE_READ).sendToTarget();
                    break;
                case MSG_TYPE_SEND:
                    String value = (String) msg.obj;
                    sendPrivate(value);
                    break;
                case MSG_TYPE_READ:
                    read();
                    break;
            }
            return false;
        }
    }

    /**
     * 读取数据线程
     */
    private class ReadDataThread extends Thread{
        @Override
        public void run() {
            while (isRun) {
                try {
                    if(in.available() > 0){
                        callbackReceive(in);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(!isExceptionClosedClient){
                callbackDisconnected();
            }
        }
    }

    /**
     * 检查连接是否存活线程
     */
    private class CheckAliveThread extends Thread{
        @Override
        public void run() {
            while (TcpClientItem.this.isAlive()){
                SystemClock.sleep(500);
            }
            exceptionCloseClient(new ClientOffLineException());
        }
    }

    public TcpClientItem(Socket socket, OnClientStatus onClientStatus) {
        client = socket;
        address = socket.getInetAddress().getHostAddress();
        this.onClientStatus = onClientStatus;
    }

    public void start(){
        HandlerThread handlerThread = new HandlerThread("tcp_client " + address);
        handlerThread.start();
        //子线程Handler
        childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
        childHandler.obtainMessage(MSG_TYPE_INIT).sendToTarget();
    }


    private void init() {
        try {
            out = new DataOutputStream(client.getOutputStream());
            in = client.getInputStream();
            isRun = true;
            isExceptionClosedClient = false;
        } catch (IOException e) {
            DoneLogger.e(TAG, "获取接入服务端的tcp客户端IO流失败");
            e.printStackTrace();
            callbackError(e);
        }
    }

    public void send(String data) {
        if(childHandler != null){
            childHandler.obtainMessage(MSG_TYPE_SEND,data).sendToTarget();
        }else{
            callbackSendMsgFailure(new ClientOffLineException());
        }
    }

    /**
     * 检察客户端是否还连接着
     * 缺陷:
     * 这种方法在本端无网络的情况下导致的连接失败十分灵敏，但是如果客户端失败重连，两边信号不统一，很可能客户端
     * 明明是连接状态，检测结果却是未连接。有时客户端断开连接2分钟，这边才检测到。
     * @return
     */
    public boolean isAlive(){
        if(client == null){
            return false;
        }

        try{
            //发送1个字节的紧急数据，默认情况下，服务器端没有开启紧急数据处理，不影响正常通信
            client.sendUrgentData(0xFF);
            return true;
        }catch(Exception se){
            return false;
        }
    }


    private void read(){
        callbackJoin();
        //读取必需单独开一个线程，否则while会阻塞创建的HandlerThread。
        new ReadDataThread().start();
//        new CheckAliveThread().start();
    }

    private void sendPrivate(String data) {
        try {
            DoneLogger.d(TAG, "发送客户端数据:" + data);
            out.write(data.getBytes(java.nio.charset.StandardCharsets.UTF_8));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 正常关闭
     */
    public void closeClient() {
        isRun = false;
        if (in != null) {
            try {
                in.close();
                in = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (out != null) {
            try {
                out.close();
                out = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (client != null) {
            try {
                client.close();
                client = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(childHandler != null){
            childHandler.removeCallbacksAndMessages(null);
            //TODO childHandler可能为null
            childHandler.getLooper().quitSafely();
            childHandler = null;
        }
    }

    public void release(){
        closeClient();
    }

    /**
     * 异常关闭释放客户端
     * @param e
     */
    public void exceptionCloseClient(Throwable e){
        DoneLogger.e(TAG,"exceptionCloseClient：" + e.getMessage());
        //防止线程没有关闭
        if(isRun){
            isExceptionClosedClient  = true;
            closeClient();
            callbackDisconnect(e);
        }
    }


    private void callbackJoin() {
        if (onClientStatus != null) {
            onClientStatus.onJoined(this);
        }
    }

    private void callbackDisconnected() {
        if (onClientStatus != null) {
            onClientStatus.onDisconnected(this);
        }
    }

    private void callbackDisconnect(Throwable e) {
        if (onClientStatus != null) {
            onClientStatus.onDisconnect(this,e);
        }
    }

    private void callbackError(Throwable e) {
        if (onClientStatus != null) {
            onClientStatus.onError(this,e);
        }
    }

    private void callbackReceive(InputStream in) {
        if (onClientStatus != null) {
            onClientStatus.onReceive(this, in);
        }
    }

    private void callbackSendMsgFailure(Throwable e) {
        if (onClientStatus != null) {
            onClientStatus.onSendMsgFailure(this, e);
        }
    }


    public interface OnClientStatus {
        /**
         * 客户端已经接入/连接
         * @param client
         */
        void onJoined(TcpClientItem client);

        /**
         * 客户端正常断开连接(主动断开)
         * @param client
         */
        void onDisconnected(TcpClientItem client);

        /**
         * 客户端异常断开连接(被动断开)
         * @param client
         */
        void onDisconnect(TcpClientItem client, Throwable e);

        /**
         * 客户端发生错误
         * @param client
         * @param e
         */
        void onError(TcpClientItem client, Throwable e);

        /**
         * 收到客户端消息
         * @param client
         * @param in
         */
        void onReceive(TcpClientItem client, InputStream in);

        /**
         * 发送消息失败
         * @param client
         * @param e
         */
        void onSendMsgFailure(TcpClientItem client, Throwable e);
    }

    public String getAddress() {
        return address;
    }
}